package repo

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"github.com/redis/go-redis/v9"
	"github.com/star-find-cloud/star-chat/pkg/database"
	log "github.com/star-find-cloud/star-chat/pkg/logger"
	"github.com/star-find-cloud/star-chat/root/domain"
	"time"
)

type RootRepoImpl struct {
	db    database.Database
	cache database.Redis
}

func NewRootRepo(db database.Database, cache database.Redis) domain.RootRepo {
	return &RootRepoImpl{
		db:    db,
		cache: cache,
	}
}

func (r *RootRepoImpl) Create(ctx context.Context, root *domain.Root) (int64, error) {
	sqlStr := "insert into star_chat.public.root(name, password, department, position, id_card, country_code, phone, email, status, level, created_time) values($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, now())"

	result, err := r.db.GetDB().ExecContext(ctx, sqlStr,
		root.Name,
		root.Password,
		root.Department,
		root.Position,
		root.IDCard,
		root.CountryCode,
		root.Phone,
		root.Email,
		root.Status,
		root.Level,
	)
	if err != nil {
		log.AppLogger.Errorf("create root failed: %v", err)
		return 0, fmt.Errorf("create root failed: %v", err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		log.AppLogger.Errorf("get root id failed: %v", err)
		return 0, fmt.Errorf("get root id failed: %v", err)
	}

	return id, nil
}

func (r *RootRepoImpl) Get(ctx context.Context, id int64) (*domain.Root, error) {
	sqlStr := "select id, name, department, position, country_code, phone, email, status, last_login, level, created_time from star_chat.public.root where id = $1"
	root := &domain.Root{}

	err := r.db.GetDB().GetContext(ctx, root, sqlStr, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get root failed: %v", err)
		return nil, fmt.Errorf("get root failed: %v", err)
	}

	return root, nil
}

func (r *RootRepoImpl) GetDetails(ctx context.Context, id int64) (*domain.Root, error) {
	sqlStr := "select * from star_chat.public.root where id = $1"
	root := &domain.Root{}

	err := r.db.GetDB().GetContext(ctx, root, sqlStr, id)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get root failed: %v", err)
		return nil, fmt.Errorf("get root failed: %v", err)
	}

	return root, nil
}

func (r *RootRepoImpl) GetByName(ctx context.Context, name string) (*domain.Root, error) {
	sqlStr := "select id, name, department, position, country_code, phone, email, status, last_login, level, created_time from star_chat.public.root where name = $1"
	root := &domain.Root{}

	err := r.db.GetDB().GetContext(ctx, root, sqlStr, name)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get root failed: %v", err)
		return nil, fmt.Errorf("get root failed: %v", err)
	}

	return root, nil
}

func (r *RootRepoImpl) GetDetailsByName(ctx context.Context, name string) (*domain.Root, error) {
	sqlStr := "select * from star_chat.public.root where name = $1"
	root := &domain.Root{}

	err := r.db.GetDB().GetContext(ctx, root, sqlStr, name)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get root failed: %v", err)
		return nil, fmt.Errorf("get root failed: %v", err)
	}

	return root, nil
}

func (r *RootRepoImpl) GetByPhone(ctx context.Context, phone, countryCode int64) (*domain.Root, error) {
	sqlStr := "select id, name, department, position, country_code, phone, email, status, last_login, level, created_time from star_chat.public.root where phone = $1 and  country_code = $2"
	root := &domain.Root{}

	err := r.db.GetDB().GetContext(ctx, root, sqlStr, phone, countryCode)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get root failed: %v", err)
		return nil, fmt.Errorf("get root failed: %v", err)
	}

	return root, nil
}

func (r *RootRepoImpl) GetAll(ctx context.Context) ([]*domain.Root, error) {
	sqlStr := "select * from star_chat.public.root"
	var roots []*domain.Root

	err := r.db.GetDB().SelectContext(ctx, roots, sqlStr)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get all root failed: %v", err)
		return nil, fmt.Errorf("get all root failed: %v", err)
	}

	return roots, nil
}

func (r *RootRepoImpl) GetDetailsAll(ctx context.Context) ([]*domain.Root, error) {
	sqlStr := "select * from star_chat.public.root"
	var roots []*domain.Root

	err := r.db.GetDB().SelectContext(ctx, roots, sqlStr)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			log.SQLLogger.Warnf("root not found: %v", err)
			return nil, fmt.Errorf("root not found: %v", err)
		}
		log.AppLogger.Errorf("get all root failed: %v", err)
		return nil, fmt.Errorf("get all root failed: %v", err)
	}

	return roots, nil
}

func (r *RootRepoImpl) Update(ctx context.Context, root *domain.Root) error {
	sqlStr := "update star_chat.public.root set name = $1, department = $2, position = $3, country_code = $4, phone = $5, email = $6, updated_time = now() where id = $7"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr,
		root.Name,
		root.Department,
		root.Position,
		root.CountryCode,
		root.Phone,
		root.Email,
		root.ID,
	)
	if err != nil {
		log.AppLogger.Errorf("update root failed: %v", err)
		return fmt.Errorf("update root failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) UpdatePassword(ctx context.Context, id int64, oldPassword, newPassword string) error {
	sqlStr := "update star_chat.public.root set password = $1, updated_time = now() where id = $2 and password = $3"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, newPassword, id, oldPassword)
	if err != nil {
		log.AppLogger.Errorf("update root password failed: %v", err)
		return fmt.Errorf("update root password failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) UpdateLevel(ctx context.Context, id int64, level int32) error {
	sqlStr := "update star_chat.public.root set level = $1, updated_time = now() where id = $2"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, level, id)
	if err != nil {
		log.AppLogger.Errorf("update root level failed: %v", err)
		return fmt.Errorf("update root level failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) Enable(ctx context.Context, id int64, status string) error {
	sqlStr := "update star_chat.public.root set status = 'enable', updated_time = now() where id = $1"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, status, id)
	if err != nil {
		log.AppLogger.Errorf("update root status failed: %v", err)
		return fmt.Errorf("update root status failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) Disable(ctx context.Context, id int64, status string) error {
	sqlStr := "update star_chat.public.root set status = 'disabled', updated_time = now() where id = $1"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, status, id)
	if err != nil {
		log.AppLogger.Errorf("update root status failed: %v", err)
		return fmt.Errorf("update root status failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) UpdateLastLogin(ctx context.Context, id int64) error {
	sqlStr := "update star_chat.public.root set last_login = now() where id = $1"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, id)
	if err != nil {
		log.AppLogger.Errorf("update root last login failed: %v", err)
		return fmt.Errorf("update root last login failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) UpdateIDCard(ctx context.Context, id int64, idCard string) error {
	sqlStr := "update star_chat.public.root set id_card = $1, updated_time = now() where id = $2"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, idCard, id)
	if err != nil {
		log.AppLogger.Errorf("update root id card failed: %v", err)
		return fmt.Errorf("update root id card failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) Delete(ctx context.Context, id int64) error {
	sqlStr := "update star_chat.public.root set status = 'deleted', deleted_time = now() where id = $1"

	_, err := r.db.GetDB().ExecContext(ctx, sqlStr, id)
	if err != nil {
		log.AppLogger.Errorf("delete root failed: %v", err)
		return fmt.Errorf("delete root failed: %v", err)
	}

	return nil
}

func (r *RootRepoImpl) MakeTokenCache(ctx context.Context, token string, id int64) error {
	key := fmt.Sprintf("root:%d", id)
	err := r.cache.GetCache().Set(ctx, key, token, 1*time.Hour).Err()
	if err != nil {
		log.AppLogger.Errorf("make token cache failed: %v", err)
		return fmt.Errorf("make token cache failed: %v", err)
	}
	return nil
}

func (r *RootRepoImpl) GetTokenCache(ctx context.Context, id int64) (string, error) {
	key := fmt.Sprintf("root:%d", id)
	token, err := r.cache.GetCache().Get(ctx, key).Result()
	if err != nil {
		if errors.Is(err, redis.Nil) {
			log.AppLogger.Warnf("token cache not found: %v", err)
			return "", fmt.Errorf("token cache not found: %v", err)
		}
		log.AppLogger.Errorf("get token cache failed: %v", err)
		return "", fmt.Errorf("get token cache failed: %v", err)
	}

	return token, nil
}

func (r *RootRepoImpl) DeleteTokenCache(ctx context.Context, id int64) error {
	key := fmt.Sprintf("root:%d", id)
	err := r.cache.GetCache().Del(ctx, key).Err()
	if err != nil {
		log.AppLogger.Errorf("delete token cache failed: %v", err)
		return fmt.Errorf("delete token cache failed: %v", err)
	}
	return nil
}
